class: inverse,left, middle background-image: url(data:image/png;base64,#background.png) background-size: cover <img src="data:image/png;base64,#LOGO_DIPLOMADO.png" width="500px"/> ##Módulo 4: Procesamiento masivo de datos geoespaciales ###Procesamiento masivo de datos: Paralelización en R José A. Lastra<br> <a href="http://github.com/JoseLastra"> Github: JoseLastra</a><br> <a href="mailto:jose.lastra@pucv.cl"> jose.lastra@pucv.cl</a><br> .large[<b><a href="https://www.pucv.cl/uuaa/site/edic/base/port/labgrs.html">LabGRS</a> | Noviembre 2022</b>] <br> --- class: center,middle background-image: url(data:image/png;base64,#labgrs_logo.png) background-size: 35% --- ## Contenidos .pull-left[ - Análisis masivo iterativo: * loops * Familia apply - Paralelización: * LibrerÃa parallel * LibrerÃa foreach y doParallel ] .pull-right[ <img src="data:image/png;base64,#https://raw.githubusercontent.com/allisonhorst/stats-illustrations/main/rstats-artwork/r_rollercoaster.png" width="650px"/> © Allison Horst ] --- ## Análisis iterativo: loops -- - En muchas ocasiones, veremos la necesidad de realizar la misma tarea muchas veces para diferentes sets de datos. -- - Algunos ejemplos: reproyectar información, realizar diversos modelos lineales, cortar datos a un área de estudio, etc. -- - Los **loops** son la definición concreta de **ejecutar una tarea n cantidad de veces**. -- - Nosotros mostraremos principalmente el uso de **ciclos for** <center><img src="data:image/png;base64,#https://www.blasbenito.com/post/02_parallelizing_loops_with_r/featured.png" width="500px"/></center> <center><a href="https://www.blasbenito.com/post/02_parallelizing_loops_with_r/">©Blas M. Benito</a></center> --- ## Análisis iterativo: loops -- - Pensemos en una tarea ya realizada durante el diplomado: Apertura de archivos -- ```r # crear objeto para primer archivo df_1 <- read_csv("TABLAS_LOOP/Tmean_Cauquenes.csv") df_1 %>% head() ``` ``` ## # A tibble: 6 × 4 ## Date Year Month Tmed ## <date> <dbl> <dbl> <dbl> ## 1 1979-01-15 1979 1 19.7 ## 2 1979-02-15 1979 2 19.1 ## 3 1979-03-15 1979 3 17.4 ## 4 1979-04-15 1979 4 14.6 ## 5 1979-05-15 1979 5 12.2 ## 6 1979-06-15 1979 6 8.54 ``` -- <center><b>¿Cómo podemos abrir todos los archivos?</b></center> --- ## Análisis iterativo: recordando [Ver](https://labgrs.github.io/01_Intro_R/Intro_R.html#63) <center><img src="data:image/png;base64,#loops.png" width="800px"/></center> <center><a href="https://labgrs.github.io/01_Intro_R/Intro_R.html#63">© MatÃas Olea</a></center> -- - Construyamos el ciclo que abra todos los archivos y los ponga en un objeto cada uno. -- - Usaremos una **lista** como estructura receptora. --- ## Análisis iterativo: loops ```r lista_archivos <- list.files( path = "TABLAS_LOOP/", # ruta de los archivos pattern = "*.csv", # patrón para listar elementos full.names = T) # ruta completa lista_df <- list() # lista vacÃa para ingresar cada tabla individual # start loop ----- for (i in 1:length(lista_archivos)) { # seteando duración del loop lista_df[[i]] <- read_csv(lista_archivos[i], # metiendo el objeto en la lista show_col_types = FALSE ) cat("tabla", i, "leÃda \n") # control de ansiedad } ``` ``` ## tabla 1 leÃda ## tabla 2 leÃda ## tabla 3 leÃda ## tabla 4 leÃda ## tabla 5 leÃda ## tabla 6 leÃda ## tabla 7 leÃda ## tabla 8 leÃda ## tabla 9 leÃda ## tabla 10 leÃda ``` --- ## Análisis iterativo: loops -- - ¿Y si queremos unir solo la columna de temperatura a una gran tabla? -- ```r id <- substr(x = lista_archivos, start = 19, stop = nchar(lista_archivos) - 4) # id's lista_df <- list() # lista vacÃa para ingresar cada tabla individual # start loop ----- for (i in 1:length(lista_archivos)) { # seteando duración del loop lista_df[[i]] <- read_csv(lista_archivos[i], # metiendo el objeto en la lista show_col_types = FALSE) %>% mutate(ID = id[i]) #agregando nuevo campo cat("tabla", i, "leÃda \n") # control de ansiedad } ``` ``` ## tabla 1 leÃda ## tabla 2 leÃda ## tabla 3 leÃda ## tabla 4 leÃda ## tabla 5 leÃda ## tabla 6 leÃda ## tabla 7 leÃda ## tabla 8 leÃda ## tabla 9 leÃda ## tabla 10 leÃda ``` --- ## Análisis iterativo: loops -- - Con la lista generada y los archivos leÃdos, podemos integrar nuestra tabla -- ```r tabla_final <- lista_df %>% bind_rows() ```
--- ## Análisis iterativo: loops -- - Una vez obtenido el resultado, podemos operar como sea requerido. ```r tabla_final %>% ggplot(aes(x = Date, y = Tmed, color = ID)) + geom_line() + facet_wrap(~ID,ncol = 2) ``` --- <img src="data:image/png;base64,#paralelizacion_files/figure-html/unnamed-chunk-8-1.png" width="100%" /> --- ## Análisis iterativo: Familia *apply -- - En general, muchos usuarios experimentados promueven **no usar** ciclos for, principalmente por motivos de eficiencia computacional (Grolemund, 2014). -- - Aunque como vimos, son una solución rápida, fácil de escribir, debuggear y entender para cualquier programador. -- - Suelen ser algo ineficientes al momento de trabajar con un alto número de datos dada su naturaleza iterativa. -- - Aquà es donde la familia de **funciones apply** entran como una alternativa al uso de ciclos for. -- - Aunque también uno puede transformar un ciclo for en un código vectorizado ([para saber más](https://rstudio-education.github.io/hopr)) --- ## Análisis iterativo: Familia *apply -- - Lo primero que uno podrÃa cuestionarse es: **¿cuál es la diferencia entre esta familia de funciones y un ciclo for?** -- - La colección *apply viene incorporada en R y puede ser alimentada por diferentes funciones para aplicar procesos redundantes sobre diferentes objetos. -- - En esencia, estas funciones buscan que el usuario evite la construcción de ciclos. -- - Las funciones correspondientes a esta familia son ([Mendoza, 2018](https://bookdown.org/jboscomendoza/r-principiantes4/)): .pull-left[ * apply() * eapply() * lapply() * mapply() ] .pull-right[ * rapply() * sapply() * tapply() * vapply() ] -- - Para fines operativos, mostraremos la aplicación de **apply()** y **lapply()** con nuestros datos empleados para los análisis de clúster. ```r datos_cluster <- read.csv('Datos_cluster.csv', sep = ',', header = T) %>% mutate(ID = paste0('A-', c(1:500))) ``` --- ## apply() -- - Es una función aplicable sobre matrices o data frames siguiendo la estructura: `apply(x, MARGIN, FUN, ...)` **x**: es una matriz de datos o data frame <br> **MARGIN**: corresponde a sobre qué realizaremos la operación. 1 Representa las filas y 2 representa las columnas <br> **FUN**: Función a aplicar sobre nuestros datos. <center><img src="data:image/png;base64,#apply.png" width="500px"/></center> <center>DATACAMP</center> --- ## apply() -- - Calculemos el promedio de todas nuestras columnas ```r tabla_app <- apply(datos_cluster[,1:8], 2, FUN = mean, na.rm = T) tabla_app ``` ``` ## X Y PP Productivity Elevation T_min ## -70.82784 -32.50630 800.93656 3.18256 1642.37400 3.42342 ## T_med T_max ## 9.88018 16.33688 ``` -- - Por defecto, apply nos entregará un vector númerico de salida. -- - Podemos hacer esto con un ciclo for, pero es menos eficiente. --- ## apply() -- - También podemos aplicar lógica **tidyverse**, donde la salida será un tibble-data.frame -- ```r tabla_tidy <- datos_cluster %>% #incoporo clusters a la tabla summarise( across( where(is.numeric), #condicion de que el campo sea numerico list(mean = mean) #lista de funciones a aplicar, si el campo es numerico ) ) ```
--- <img src="data:image/png;base64,#paralelizacion_files/figure-html/unnamed-chunk-13-1.png" width="100%" /> ``` ## Unit: microseconds ## expr min lq mean median uq max neval ## tidy_version 2352.6 2951.05 3285.279 3114.15 3430.5 8155.9 100 ## apply_version 214.9 258.80 326.424 305.55 347.1 857.7 100 ``` --- ## lapply() -- - La función **lapply()** es considerada un caso especial de **apply()**. -- - Su objetivo es aplicar funciones a todos los elementos presentes en una lista. -- - Esta función, es mucho más flexible que **apply()** ya que en R prácticamente todo puede ser transformado en una lista. -- - La salida de **lapply()** siempre será una lista, que puede ser transformada en otro objeto posteriormente. -- - **lapply()** es un primer paso a la paralelización de procesos complejos en R. --- ##lapply() -- - Consideremos el ejemplo dispuesto en la página [16](#16): **Abrir múltiples archivos** -- - La estructura con **lapply()** serÃa la siguiente: ```r lista_df <- list.files(path = 'TABLAS_LOOP/', pattern = '*.csv', full.names = T) %>% lapply(read_csv, show_col_types = F) ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## for_version 117.7856 120.2245 123.9520 122.7695 126.7190 135.5615 30 ## lapply_version 115.3609 117.9628 122.6526 120.9864 128.3788 133.2082 30 ``` -- - Como se aprecia, ganamos algo de velocidad en el procesamiento y disminuÃmos de forma importante la cantidad de lÃneas de código para realizar la misma tarea. --- ## Paralelización: Conceptos -- - Entonces, si podemos lograr el procesamiento masivo con las funciones o ejemplos vistos antes...**¿Por qué paralelizar?** <center><img src="data:image/png;base64,#cpu.gif" width="550px"/></center> <center>© Andreas Angourakis</center> -- - Los procesos de serialización solo aprovechan uno de los núcleos de nuestros computadores. --- ## Paralelización: Conceptos -- - En general, la mayorÃa de los códigos en R funcionan rápido y bien en un solo procesador o núcleo. -- - Sin embargo, a veces podemos encontrar ciertos problemas de cálculo vinculados a ([Jones, 2017](https://nceas.github.io/oss-lessons/parallel-computing-in-r/parallel-computing-in-r.html)): * **CPU**: proceso toma mucho tiempo de CPU. * **Memoria**: Demanda demasiada memoria. * **I/O**: toma mucho tiempo la lectura/escritura desde el disco. * **Network**: La red toma demasiado tiempo en transferir. -- - La paralelización viene a "solucionar" los problemas asociados a la CPU, dada la presencia de múltiples núcleos en los pc's modernos. -- - Los cuales, a su vez, tienen más memoria disponible para trabajar. --- ## Paralelización: Conceptos -- - Existen variadas librerÃas para ejecutar procesos paralelizando en R ([**ver más**](https://cran.r-project.org/web/views/HighPerformanceComputing.html)) -- - Estas en general se valen de los siguientes métodos de paralelización: * **Fork**: que copia el proceso de R a un nuevo núcleo, compartiendo el entorno. **No está disponible para Windows** * **Socket**: Lanza una versión nueva de R en cada uno de los núcleos de procesamiento. Similar a trabajar en un cluster de equipos conectados en red. --- ## Paralelización: Conceptos <center><img src="data:image/png;base64,#sock_fork.png" width="900px"/></center> <center>Fuente: Benito, 2021</center> --- ## Paralelización: Conceptos -- - Para ambos métodos podemos encontrar pros y contras: .pull-left[ **Socket** * Pro: Funciona en cualquier sistema operativo * Pro: No hay contaminación, porque cada nodo es individual * Con: es más lento que el método **Fork** * Con: Las variables, paquetes y demás, deben ser explÃcitamente puestos en cada nodo * Con: más difÃcil de implementar ] .pull-right[ **Fork** * Pro: Más rápido que el **Socket** * Pro: Al copiar la versión de R existente, todo el entorno existe en cada nodo * Pro: Más simple de implementar * Con: Solo funciona en sistemas POSIX (Max, Linux, Unix, BSD) * Con: Al operar sobre procesos duplicados, puede causar comportamientos extraños en ciertas ejecuciones. ] <br> -- - Nosotros nos enfocaremos en el uso de **Socket** en esta sesión. --- ## Paralelización: Aplicación k-means. - Para ejemplificar el proceso de paralelización, usaremos los datos del archivo **Datos_cluster.csv** leÃdos y guardados en el objeto **datos_cluster**. -- ```r rownames(datos_cluster) <- datos_cluster$ID #asignar nombres de estacion a filas estaciones_cluster <- datos_cluster %>% na.omit() #limpiar filas con NA's ```
--- ## Paralelización: Aplicación k-means. -- - Preparación de datos ```r #### normalización de los datos ---- estaciones_scale <- estaciones_cluster %>% dplyr::select(-c(ID,X,Y)) # sacamos coordenadas e ID estaciones_scale <- scale(estaciones_scale) ``` -- - Como estos datos se emplearon en los ejercicios, sabemos que el número óptimo de clústers es de 18. -- - Y configuraremos los centros empleando 100 posiciones iniciales. ```r ### k-means ---- estaciones_k <- kmeans(estaciones_scale, #objeto a clusterizar centers = 18, #centros nstart = 100, #número de inicios iter.max = 100) #para asegurar convergencia del modelo ``` --- ## Paralelización: Aplicación k-means. -- - Nuestro resultado, **estaciones_k**, dispone de un elemento denominado **total within-cluster sum of square** (*estaciones_k$tot.withinss*) para todos nuestros inicios. -- - Recordemos que este valor nos indica que tan buena es nuestra clusterización (debe ser un valor bajo). -- - Ahora veremos algunas alternativas para paralelizar el proceso y visualizar nuestros resultados. <center><img src="data:image/png;base64,#https://raw.githubusercontent.com/allisonhorst/stats-illustrations/main/other-stats-artwork/debugging.jpg" width="500px"/></center> <center>©Allison Horst</center> --- ## 1/2 Paralelización: lapply(). -- - Esta forma de paralelizar está dentro de las más intuitivas y nos dará un acercamiento claro al proceso que realizaremos después. -- - Para esto, construiremos una función de prueba que nos permita paralelizar adecuadamente el cálculo. ```r #creación de la función kmeans_parallel <- function(x){ kmeans(estaciones_scale, #datos directo para simplificar centers = 18, # centros nstart = x, #variable que cambiará iter.max = 100) #para asegurar convergencia } ``` .footnote[ ejemplo basado en [Lockwood (2014)](https://www.glennklockwood.com/data-intensive/r/lapply-parallelism.html#3-introduction) y [Orellana (2018)](https://bookdown.org/content/1498/) ] --- ## 1/2 Paralelización: lapply(). -- - Ahora aplicaremos nuestra función, considerando 4 ejecuciones con: 25 como **nstart** para obtener los mismos 100 inicios. ```r #aplicación función con lapply results_k_parallel <- lapply(c(25, 25, 25, 25), FUN = kmeans_parallel) # uso de sapply para extraer valores de tot.withinss temp_vector <- sapply(results_k_parallel, #resultados kmeans function(result) {result$tot.withinss} #función ) mejor_resultado <- results_k_parallel[[which.min(temp_vector)]] #selección mejor resultado ``` --- ## 1/2 Paralelización: lapply(). <img src="data:image/png;base64,#paralelizacion_files/figure-html/unnamed-chunk-22-1.png" width="100%" /> ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## serial 58.4611 59.6056 60.99598 60.39105 61.43590 66.5937 20 ## para_lapply 58.3175 61.1435 63.55163 62.62390 64.81105 72.8106 20 ``` --- ## 1/2 Paralelización: lapply(). -- - Lo que realizamos en este ejemplo fue lo siguiente: <center><img src="data:image/png;base64,#lapply_par.png" width="500px"/></center> <center>LabGRS, 2022</center> --- ## Paralelización: librerÃa parallel -- - Como se mencionó anteriormente, existen diversas librerÃas para el procesamiento paralelizado en R. -- - Dos de las más conocidas y poderosas para esta tarea son **multicore** y [**snow**](https://CRAN.R-project.org/package=snow ). -- - Ambas fueron fusionadas e incluÃdas en la instalación de R base a través de la librerÃa **parallel** desde la versión de R 2.14.0 (2011). -- - Esta librerÃa considera los aspectos de paralelización **Socket** y **Forking** descritos con anterioridad. -- - Incluye versiones paralelizadas de la familia *apply -- - En el siguiente ejemplo usaremos la versión paralelizada de lapply(), **parLapply()** para llevar a cabo la clusterización de nuestros datos. -- - Para usar el estilo de paralelismo de parLapply, debemos emplear la conexión tipo red (**Socket**) mencionada anteriormente. --- ## Paralelización: parLapply() -- - Primer paso: setear los núcleos de trabajo ```r library(parallel) #carga de librerÃa #detectar el número de núcleos disponibles nucleos <- detectCores(logical = F)-1 # logical = F, uso de núcleos fÃsicos ``` -- - Segundo paso: crear el clúster y exportar los datos y variables necesarias a todos los CPU's de forma explÃcita. ```r cl <- makeCluster(nucleos, type = 'PSOCK') #creación del cluster de procesamiento clusterExport(cl, c('estaciones_scale')) ``` -- - En este caso, solo debemos exportar el dataset para que la función lo utilice. --- ## Paralelización: parLapply() -- - Tercer paso: aplicar función y cerrar el clúster. ```r results_parLapply <- parLapply(cl, #cluster c(25,25,25,25), #parámetros de inicio fun = kmeans_parallel) #Función a aplicar stopCluster(cl) #uso de sapply para extraer valores de tot.withinss temp_vector <- sapply(results_parLapply, #resultados kmeans function(result) {result$tot.withinss} #función ) mejor_resultado <- results_parLapply[[which.min(temp_vector)]] #selección mejor resultado ``` --- ## Paralelización -- - Si comparamos los resultados obtenidos de los tres tipos de procesamiento, ver una baja importante en el tiempo de procesamiento al paralelizar. ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## noCluste_ver 55.5895 59.3358 60.92229 60.24595 62.2509 71.7625 30 ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## lapply_ver 59.6719 62.249 63.8216 63.37965 64.7217 71.8104 30 ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## parLapply_ver 17.5803 18.8874 20.35915 19.40895 21.5966 26.8334 30 ``` -- - Lo relevante a considerar cuando se paraleliza un proceso: * Al copiar los datos y variables a cada CPU, se empleará tiempo y memoria. * La creación de los clúster por parte del sistema, también consumirá tiempo. * En el ejemplo, el número de datos trabajado es bajo y no es demasiado el impacto de ejecutar el código sin paralelizar. * **No todo puede ser paralelizado**. --- ## Paralelización: librerÃa foreach y doParallel -- - Ahora veremos el uso de la librerÃa [**foreach**](https://CRAN.R-project.org/package=foreach), que proporciona una forma distinta de ejecutar procesos iterativos en R. -- - ¿Si ya existen los ciclos for para qué utilidar esta librerÃa? **Respuesta**: foreach permite la ejecución paralelizada cuando es complementada con la librerÃa [**doParallel**](https://CRAN.R-project.org/package=doParallel). -- - Instale ambas librerÃas empleando `install.packages(c('foreach','doParallel'))` y cárguelas en su entorno de trabajo. ```r library(foreach) library(doParallel) ``` --- ## Paralelización: librerÃa foreach y doParallel -- - Operación básica de foreach: Calculemos la raÃz cuadrada de 3 valores -- ```r foreach(i=1:3) %do% sqrt(i) ``` ``` ## [[1]] ## [1] 1 ## ## [[2]] ## [1] 1.414214 ## ## [[3]] ## [1] 1.732051 ``` -- - La función **foreach()** se parece a un ciclo for, pero se implementa con el operador ` %do%`. -- - Además, nos devuelve un valor que por defecto es una lista. Esto nos da la flexibilidad de complementar la salida con otros flujos de trabajo. --- ## Paralelización: librerÃa foreach y doParallel -- - Veamos otro ejemplo con dos variables: -- ```r foreach(a = 1:3, b = rep(10, 3)) %do% { a + b } ``` ``` ## [[1]] ## [1] 11 ## ## [[2]] ## [1] 12 ## ## [[3]] ## [1] 13 ``` -- - Aquà **a** y **b** son *"variables de iteración"* dado que cambian durante las múltiples ejecuciones. --- ## Paralelización: librerÃa foreach y doParallel -- - La salida por defecto de foreach es una **lista**, pero podemos especificar otros tipos de salida empleando el argumento `.combine` ```r #vector foreach(i = 1:3, .combine = 'c') %do% exp(i) ``` ``` ## [1] 2.718282 7.389056 20.085537 ``` ```r #matriz foreach(i = 1:2, .combine = 'cbind') %do% rnorm(4) ``` ``` ## result.1 result.2 ## [1,] 0.08726137 -1.49433026 ## [2,] 0.41816981 -2.75010746 ## [3,] -0.67540588 -0.05124722 ## [4,] 1.47174889 1.69841297 ``` -- - De igual forma, podemos emplear **rbind()** o incluso funciones propias apliquen algo a nuestros resultados intermedios. --- ## Paralelización: librerÃa foreach y doParallel -- - En la práctica, foreach equivalente al paralelismo basado en lapply revisado anteriormente. -- - La diferencia, es que expone la paralelización directamente a nosotros, haciéndolo más intuitivo: * No es necesario evaluar una función en cada objeto de entrada, ya que el código contenido en el cuerpo de **foreach** es el que se ejecuta en cada objeto * El código paralelizado emplea la misma sintáxis a través de todos los entornos, por lo que no hay necesidad de cambiar entre miembros de la familia apply (lapply, parLapply, mclapply) ```r mi_lista <- c(1, 2, 3, 4, 5) #valores output_1 <- lapply(mi_lista, FUN = function(x) { y = x + 1; y } ) output_2 <- foreach(x = mi_lista) %do% { y = x + 1; y } #comparación unlist(output_1) == unlist(output_2) ``` ``` ## [1] TRUE TRUE TRUE TRUE TRUE ``` --- ## Paralelización: librerÃa foreach y doParallel -- - Apliquemos por última vez nuestro k-means empleando esta forma de paralelizar ```r results_foreach <- foreach( i = c(25,25,25,25) ) %do% { kmeans(x = estaciones_scale, centers = 4, nstart = i) } #uso de sapply para extraer valores de tot.withinss temp_vector <- sapply(results_foreach, #resultados kmeans function(result) {result$tot.withinss} #función ) mejor_resultado <- results_foreach[[which.min(temp_vector)]] #selección mejor resultado ``` ``` ## Unit: milliseconds ## expr min lq mean median uq max neval ## foreach_test 23.7176 25.2254 26.31313 25.52585 26.7254 34.952 30 ``` --- ## foreach y doParallel: Ejemplo práctico con rásters -- - Crearemos una lista de archivos **MOD13Q1** correspondientes al Ãndice EVI desde el año 200 hasta el 2021. -- - Ejecutaremos dos tareas simples: (1) cortar y (2) enmascarar datos a un área de estudio. -- - Usaremos los siguientes archivos: (1) **san_javier.gpkg**, (2) **MOD13Q1_dates_ts492.csv** y (3) **MOD13Q1_bands (carpeta)** ```r ## Listando archivos bandas <- list.files(path = 'MOD13Q1_bands/',pattern = '*.tif',full.names = T) ## archivo de fechas tabla <- read_csv('MOD13Q1_dates_ts492.csv') ## shape de San Javier shp <- read_sf('san_javier.gpkg') ``` --- ## foreach y doParallel: Ejemplo práctico con rásters -- - Preparación de clúster de procesamiento. ```r ## nombres de salida archivos nombres <- paste0('San_Javier_MOD13Q1/', tabla$dates,'_', tabla$ID,'_San_Javier.tif') ## Cluster set up nCluster <- detectCores(logical = F)-1 #Número de núcleos fÃsicos cl <- makeCluster(nCluster,type = 'PSOCK') #Configuración del cluster registerDoParallel(cl) #registrar para doParallel ``` --- ## foreach y doParallel: Ejemplo práctico con raster -- - Configuración de foreach y detención del clúster ```r foreach (i= 1:length(nombres)) %dopar% { #importar librerÃas a los nodos library(raster) library(magrittr) r <- bandas[i] %>% raster() #crear banda en R m <- r %>% crop(shp) %>% mask(shp) # #cortar y enmascarar #guardar resultado writeRaster(m, filename = nombres[i], overwrite=T, datatype = 'INT2S') } stopCluster(cl) ``` --- <center><img src="data:image/png;base64,#cpu.png" width="950px"/></center> <center>LabGRS, 2022</center> --- ## terra y la paralelización -- - Los ejemplos de paralelización vistos anteriormente, donde se envÃa información a cada nodo, no son compatibles con los objetos de la librerÃa **terra** ([ver más, pág. 6](https://cran.r-project.org/web/packages/terra/terra.pdf)) -- - terra provee funciones que permiten realizar operaciones empleando múltiples núcleos. Siendo **app()** la más común y simple de emplear. ```r evi_sj <- list.files(path = 'San_Javier_MOD13Q1/', pattern = '*.tif', full.names = T) %>% rast() ``` --- ## app() -- - Esta función nos permite operar en un **SpatRaster** multi o monobanda para aplicar una función normalmente de resumen (sum, min, max, median, etc.). También podemos operar en **SpatRasterDataset**. -- - **Importante**: dado la eficiencia computacional de **terra** muchas funciones resumen pueden ser aplicadas directamente con un funcionamiento óptimo. -- - Sin embargo, para archivos muy grandes puede ser útil la aplicación de **app()** ```r mean_noApp <- evi_sj %>% mean(na.rm = T) #simple mean_app <- app(evi_sj, fun = mean, na.rm = T, cores = 5) #5 cores all.equal(mean_app,mean_noApp) #comparación ``` ``` ## [1] TRUE ``` ``` ## Unit: seconds ## expr min lq mean median uq max neval ## simple_ver 1.368807 1.37998 1.390141 1.393221 1.401831 1.406866 5 ## cores_ver 1.372363 1.37549 1.399598 1.379960 1.427770 1.442406 5 ``` -- - **app()** opera los archivos de la misma forma que **apply()**: donde cada banda es una columna en la matriz y los pÃxeles son las filas. --- ## sapp() -- - Si lo que queremos es aplicar una función a cada banda dentro de nuestro **SpatRaster**, podemos emplear *sapp()*. ```r evi <- list.files(path = 'MOD13Q1_bands/', pattern = '*.tif', full.names = T) %>% rast() shp_v <- shp %>% vect() #sf to SpatVector #versión simple evi_sj <- evi %>% crop(shp_v) %>% mask(shp_v) ``` ``` ## Unit: seconds ## expr min lq mean ## evi_sj <- evi %>% crop(shp_v) %>% mask(shp_v) 1.942035 1.96358 1.981017 ## median uq max neval ## 1.976733 1.990057 2.066754 30 ``` --- ## sapp() ```r # versión con sapp() ## Creando la función crop_mask <- function(x, ...){ r <- x %>% crop(shp_v) %>% mask(shp_v) r } ##aplicación evi_sj_sapp <- sapp(evi_sj, fun = crop_mask) ``` ``` ## Unit: seconds ## expr min lq mean ## evi_sj_sapp <- sapp(evi_sj, fun = crop_mask) 5.660717 5.836273 6.122939 ## median uq max neval ## 5.966561 6.493747 6.657398 5 ``` --- ## Observaciones -- - **sapp()** invoca a **sapply()** por detrás y aún no se ha implementado la paralelización para esta alternativa (**Información actualizada al 14 de Octubre de 2022**). -- - Tal cuál lo indica la documentación: *"(...) La paralelización es soportada, pero a menudo no ayuda, e incluso puede ser más lenta."* -- - Justificaciones para paralelizar con terra: * Disponemos de más de 8 núcleos para procesar * O tenemos una función muy compleja (lenta) para aplicar sobre nuestros archivos. -- - Muchas veces el uso de **lapply()** puede ser suficiente. --- ## lapply() con terra -- - Tomemos nuestra lista de archivos originales y la función creada anteriormente para cortar y enmascarar. ```r evi_list <- list.files(path = 'MOD13Q1_bands/', pattern = '*.tif', full.names = T) #adaptando la función para lapply crop_mask <- function(x, ...){ x <- rast(x) #SpatRaster r <- x %>% crop(shp_v) %>% mask(shp_v) #aplicación de operación rm(x) # liberar memoria r #entregar objeto } evi_crop <- lapply(evi_list,FUN = crop_mask) ``` --- ## BibliografÃa complementaria - Bosco, J. (2018). "R Para Principantes". [Ver](https://bookdown.org/jboscomendoza/r-principiantes4/) - Cao, R. y Fernández, R. (2022). "Introducción al procesamiento en paralelo en R". En: "Técnicas de Remuestreo". [Ver](https://rubenfcasal.github.io/book_remuestreo/intro-hpc.html) - Gillespie, C., & Lovelace, R. (2021). "Efficient R programming: a practical guide to smarter programming." O'Reilly Media, Inc. [Ver](https://csgillespie.github.io/efficientR/) - Hijmans, R. J., Bivand, R., Forner, K., Ooms, J., Pebesma, E., & Sumner, M. D. (2022). Package ‘terra’. [Ver](https://brieger.esalq.usp.br/CRAN/web/packages/terra/terra.pdf) - Jones, M. (2017). "Quick Intro to Parallel Computing in R". [Ver](https://nceas.github.io/oss-lessons/parallel-computing-in-r/parallel-computing-in-r.html) - McCallum, E., & Weston, S. (2011). "Parallel R". O'Reilly Media, Inc. - Orellana, J. (2018). "HPC con R para investigadores". [Ver](https://bookdown.org/content/1498/) --- class: inverse middle 